package com.lx.composetest.lowlatency
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import android.content.Context
import android.graphics.Color
import android.opengl.GLES20
import android.opengl.Matrix
import android.os.Build
import android.util.Log
import android.view.InputDevice
import android.view.MotionEvent
import android.view.SurfaceView
import androidx.annotation.RequiresApi
import androidx.annotation.WorkerThread
import androidx.graphics.lowlatency.BufferInfo
import androidx.graphics.lowlatency.GLFrontBufferedRenderer
import androidx.graphics.opengl.egl.EGLManager
import androidx.input.motionprediction.MotionEventPredictor
import com.lx.composetest.ink.InkRecorder
import java.util.LinkedList
import java.util.concurrent.atomic.AtomicInteger


@RequiresApi(Build.VERSION_CODES.Q)
class InkSurfaceView(context: Context) : SurfaceView(context) {
    private var mFrontBufferRenderer: GLFrontBufferedRenderer<FloatArray>? = null

    /**
     * LineRenderer to be created on the GL Thread
     */
    private var mLineRenderer: LineRenderer? = null
    private var mWidth: Int = 0
    private var mHeight: Int = 0
    private var mPreviousX: Float = 0f
    private var mPreviousY: Float = 0f
    private var mCurrentX: Float = 0f
    private var mCurrentY: Float = 0f
    private val LINE = 0f
    private val PREDICT = 1f
    private val motionPredictor = MotionEventPredictor.newInstance(this)


    @WorkerThread // GLThread
    private fun obtainRenderer(): LineRenderer =
        mLineRenderer ?: (LineRenderer()
            .apply {
                initialize()
                mLineRenderer = this
            })

    private val mCallbacks = object : GLFrontBufferedRenderer.Callback<FloatArray> {
        private val mMVPMatrix = FloatArray(16)
        private val mProjection = FloatArray(16)
        private val mSceneParams = ArrayList<FloatArray>()
        private var previousWipeLine: FloatArray? = FloatArray(5)
        private var previousLine: FloatArray? = FloatArray(5)
        private var previousLineQueue: LinkedList<FloatArray> = LinkedList<FloatArray>()

        override fun onDrawFrontBufferedLayer(
            eglManager: EGLManager,
            bufferWidth: Int,
            bufferHeight: Int,
            bufferInfo: BufferInfo,
            transform: FloatArray,
            param: FloatArray
        ) {
            GLES20.glViewport(0, 0, bufferWidth, bufferHeight)
            Matrix.orthoM(
                mMVPMatrix,
                0,
                0f,
                bufferWidth.toFloat(),
                0f,
                bufferHeight.toFloat(),
                -1f,
                1f
            )
            Matrix.multiplyMM(mProjection, 0, mMVPMatrix, 0, transform, 0)
            val vWidth = this@InkSurfaceView.width.toFloat()
            val vHeight = this@InkSurfaceView.height.toFloat()
//            println("矩阵")
//            mProjection.forEach {
//                print("矩阵$it:")
//            }
//            println("")
            // Draw debug lines outlining the rendering area
            with(obtainRenderer()) {
                if (param.getOrNull(4) == PREDICT) {
                    if (previousWipeLine != null) {
                        clearPreviousLine(mProjection, previousWipeLine!!, LINE_WIDTH)
                    }
                    previousWipeLine = param
                    previousLineQueue.forEach {
                        drawLines(mProjection, it, Color.BLUE, LINE_WIDTH)
                    }
                } else {
                    previousLineQueue.forEach {
                        drawLines(mProjection, it, Color.BLUE, LINE_WIDTH)
                    }
//                    previousLine = param
                    previousLineQueue.offer(param)
                    if (previousLineQueue.size > 15) {
                        previousLineQueue.poll()
                    }
                }
//                drawLines(mProjection, floatArrayOf(0f, 0f, 0f, vHeight), Color.GREEN)
//                drawLines(mProjection, floatArrayOf(0f, vHeight, vWidth, vHeight), Color.CYAN)
//                drawLines(mProjection, floatArrayOf(vWidth, vHeight, vWidth, 0f), Color.BLUE)
//                drawLines(mProjection, floatArrayOf(vWidth, 0f, 0f, 0f), Color.MAGENTA)
                drawLines(mProjection, param, Color.BLUE, LINE_WIDTH)
                Log.i("TAG", "drawLines: ${param[0]}- ${param[1]} : ${param[2]}- ${param[3]}")
            }
        }

        override fun onDrawMultiBufferedLayer(
            eglManager: EGLManager,
            bufferWidth: Int,
            bufferHeight: Int,
            bufferInfo: BufferInfo,
            transform: FloatArray,
            params: Collection<FloatArray>
        ) {
            GLES20.glViewport(0, 0, bufferWidth, bufferHeight)
            GLES20.glClearColor(1f, 1f, 1f, 1f)
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
            Matrix.orthoM(
                mMVPMatrix,
                0,
                0f,
                bufferWidth.toFloat(),
                0f,
                bufferHeight.toFloat(),
                -1f,
                1f
            )
            Matrix.multiplyMM(mProjection, 0, mMVPMatrix, 0, transform, 0)
            val realLines = params.filter { it.getOrNull(4) == LINE }
            mSceneParams.addAll(realLines)
            Log.i("TAG", "onDrawMultiBufferedLayer: ${Thread.currentThread()}")
            for (line in mSceneParams) {
                obtainRenderer().drawLines(mProjection, line, Color.BLUE, LINE_WIDTH)
            }

//            val line = floatArrayOf(300f, 300f, 700f, 1000f, LINE)
//            obtainRenderer().drawLines(mProjection, line, Color.BLUE, LINE_WIDTH)
        }

    }
    val renderCount = AtomicInteger(0)

    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            requestUnbufferedDispatch(InputDevice.SOURCE_CLASS_POINTER)
        }
        setOnTouchListener { _, event ->
            motionPredictor.record(event)
            InkRecorder.addNewTouchEvent(event)
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    this.requestUnbufferedDispatch(event)
                    mCurrentX = event.x
                    mCurrentY = event.y
                    renderCount.set(0)
                }

                MotionEvent.ACTION_MOVE -> {
                    mPreviousX = mCurrentX
                    mPreviousY = mCurrentY
                    mCurrentX = event.x
                    mCurrentY = event.y
                    val line = FloatArray(5).apply {
                        this[0] = mPreviousX
                        this[1] = mPreviousY
                        this[2] = mCurrentX
                        this[3] = mCurrentY
                        this[4] = LINE
                    }
                    renderCount.incrementAndGet()
                    mFrontBufferRenderer?.renderFrontBufferedLayer(line)
                    Log.i("InkSurfaceView", "orientation :${event.orientation}")

//                    val motionEventPredicted = motionPredictor.predict()
//                    //处理预测点位
//                    if (motionEventPredicted != null) {
//                        //仅仅使用一半的预测
//                        val predictX = mCurrentX + (motionEventPredicted.x - mCurrentX) / 2f
//                        val predictY =  mCurrentY + (motionEventPredicted.y - mCurrentY) / 2f
//                        //预测点位标识：PREDICT
//                        val predictedSegment = floatArrayOf(
//                            mCurrentX, mCurrentY,
//                            predictX,predictY, PREDICT
//                        )
//                        mFrontBufferRenderer?.renderFrontBufferedLayer(predictedSegment)
//                    }
                }

                MotionEvent.ACTION_CANCEL -> {
                    mFrontBufferRenderer?.commit()
                }

                MotionEvent.ACTION_UP -> {
                    mFrontBufferRenderer?.commit()
                }
            }
            true
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        mFrontBufferRenderer = GLFrontBufferedRenderer(this, mCallbacks)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        mWidth = right - left
        mHeight = bottom - top
    }

    //    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
//        super.onSizeChanged(w, h, oldw, oldh)
//        mWidth = w
//        mHeight = h
//    }
    override fun onDetachedFromWindow() {
        mFrontBufferRenderer?.release(true) {
            obtainRenderer().release()
        }
        super.onDetachedFromWindow()
    }

    private companion object {
        private const val LINE_WIDTH = 10f
    }
}